/*
   Copyright(c) 2010, 2011 Bryce Alcock

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.com

 */
package singleton;

import java.io.Serializable;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Double check lock safe lazy loaded singleton.
 *
 * @author Bryce.Alcock  
 *
 */
public class SingletonClass implements Serializable {

    // The single instance.
    private static SingletonClass instance;
    
    // We will not be using syncrhonized to lock, but instead Semaphores.
    // Only one user will be able to ever aquire the semaphore that allows 
    // entry into the critical construction area of the code.
    private static final Semaphore s = new Semaphore(1);

    //  We are using a double check optimistic condition variable.
    //  In theory, it is not needed to keep 2 of these around, but this will
    //  further gaurantee that byte code re-ordering cannot cuase both of these
    //  optimistic checks to fail.
    public volatile boolean isReadyInstance =false;
    volatile private static boolean isReadySingleton=false;

    public static final AtomicInteger constructorInteger = new AtomicInteger(0);
    public final  int instanceid=constructorInteger.incrementAndGet();

    public static SingletonClass getInstance(){
        // If the instance is null, we must create it or wait for another thread
        // to create it.
        if(instance==null){
            //  Aquire the semaphore only 1 thread will be able to enter this critical secion.
            //  the second thread will move down to the else.
            if(s.tryAcquire()){
                //  Construct the object.
                //  but assign it only to a stack allocated reference var.
                SingletonClass sc=new SingletonClass();
                //  prevent any re-ordering by actively checking the state of the last
                //  action to be performed in the contruction of the object.
                //  Note that the instance.isReadyInstance var starts out false.
                //  Once the instance construction is complete it is set to true.
                while(!sc.isReadyInstance){
                    try {
                        Thread.sleep(0, 10);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SingletonClass.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

                // communicate to other threads that the instance has been both created, initialized, and check for
                // a valid state.  Also assign the stack allocated reference var to the instance reference.
                
                // This should be guarenteed to be true at this point, and will prevent any byte code re-ordering that typically breaks the double check lock.
                if(sc.isReadyInstance){
                    instance =sc;
                }
                isReadySingleton=true;
            }else{
                // If we are not the first thread entering the critical section, we
                // must wait until the frist thread has created and tested the object.
                while(!isReadySingleton){
                    try {
                        Thread.sleep(0, 10);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SingletonClass.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }

                // Once the first thread has finished checking the object, we need to ensure that the object is ready.
                boolean instanceReady=false;

                while(!instanceReady){
                    try {
                        instanceReady=instance.isReadyInstance;
                        if(instanceReady){
                            break;
                        }
                        Thread.sleep(0, 100);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SingletonClass.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (Exception e){
                        Logger.getLogger(SingletonClass.class.getName()).log(Level.SEVERE, null, e);
                    }
                }
           }
        }
        return instance;
    }

    private SingletonClass(){
        
        // init goes here.
        this.isReadyInstance = true;
    }
}
